// (C) Copyright 2009, 2010 Communications Programming, Inc.
/***************************************************************************************************************
*  Name: max541.c
*
*  Description: Function to perform analog output utilizing spi interface to MAX541 chips.
*  Alternative to using PWM with RC filter.  
*
*  Calling Parameters:
*  
*	INT16 MAX541Open(	SpiChannel	spiChan,	// SPI Device, eg SPI_CHANNEL1, SPI_CHANNEL1A, etc
*						UINT32		spiFreq,	// spi frequency in Hz
*						IoPortId 	csPort,		// (out) chip select port; defined in "ports.h"; e.g. IOPORT_A, IOPORT_B, ... 
*						UINT32		csChan,		// (out) chip select pin; defined in "ports.h"; e.g. BIT_0, BIT_1, ...
*						UINT32		pbClk );	// peripheral bus clock speed
*
*	Returns > 0 handle to use in future i/o calls, -1 if failed
*
*   INT16 maxim541Write( int handle, UINT16 value );	// writes unsigned 16-bit value to maxim541 using spiDevice
*
*	Returns 0 for success, or non-0 error codes in max541.h if failed
*
*	INT16 MAX541Close( UINT16 handle );
*
*	Closes the MAX541 device
*
*	Returns 0 for success, or non-0 error codes in max541.h if failed
*
*  Revision History:
*  Date    Who  Comments
*  ------  ---  -----------------------------------------------------------------------------------------
*  100929  RLP	Created.
*  110309  RLP	Workaround for framing anomalies of 541
*
***************************************************************************************************************/

#include <stdlib.h>
#include <userLib.h>
#include <d2a\max541.h>

static UINT8 firstTime = 1;
static UINT8 openDevices[SPI_CHANNELS+1];	// holds number of open handles, by SPI channel, starting w/ 1

INT16 MAX541Open(	SpiChannel	spiChan,	// SPI Device, eg SPI_CHANNEL1, SPI_CHANNEL1A, etc
					UINT32		spiFreq,	// spi frequency in Hz
					IoPortId 	csPort,		// (out) chip select port; defined in "ports.h"; e.g. IOPORT_A, IOPORT_B, ... 
					UINT32		csChan,		// (out) chip select pin; defined in "ports.h"; e.g. BIT_0, BIT_1, ...
					UINT32		pbClk )		// peripheral bus clock speed
{
	MAX541DevInfo *devInfo;					// device info struct for this device
	UINT8		  i;

	if( firstTime )
	{
		firstTime = 0;
		for( i = 0; i <= SPI_CHANNELS; ++i )
			openDevices[i] = 0;
	}
	if( spiFreq > MAX541_MAX_SPI_FREQ )
		spiFreq = MAX541_MAX_SPI_FREQ;		// limit SPI clk to data sheet max

	// allocate a device info block

	devInfo = (MAX541DevInfo *)malloc( sizeof(MAX541DevInfo) );
	if( devInfo == NULL )			// if malloc fails, make sure adequate heap size in Project/Build Options/Project/Linker
		return(-1);
	
	// fill in devInfo block
	
	strcpy( devInfo->devName, "MAX541" );
	devInfo->spiChan = spiChan;		// spi channel connected to max541
	devInfo->spiFreq = spiFreq;		// spi frequency in Hz
	devInfo->csPort = csPort;		// (out) chip select port; defined in "ports.h"; e.g. IOPORT_A, IOPORT_B, ... 
	devInfo->csChan = csChan;		// (out) chip select pin; defined in "ports.h"; e.g. BIT_0, BIT_1, ...

	if( openDevices[spiChan] == 0 )			// no open handles; must open device
		SpiChnOpen(spiChan, 
				   SPI_OPEN_MSTEN | 		// set master mode
				   SPI_OPEN_MODE16 , 		// set 16 bits/word 			        		
				   (pbClk + spiFreq - 1)/spiFreq);			// source Clock divisor to extract the bitrate=srcClk/srcClkDiv. 
	
	++openDevices[spiChan];					// bump count of open handles using this SPI channel
	
	// Initialize DIO

	PORTSetPinsDigitalOut(devInfo->csPort, devInfo->csChan);			// output

	return( handleNumberGet((DevInfo *)devInfo) );		// return i/o handle to caller
}

INT16 MAX541Write(INT16 handle, UINT16 value)
{
	MAX541DevInfo *devInfo;					// device info struct for this device
	int			  msb;						// extracted MSB
	UINT16		  adjValue;					// shifted out val, and dummy data read back from 541

	// get the device info blk

	devInfo = (MAX541DevInfo *)handleDevInfoGet( handle );
	if( devInfo == NULL ) 
		return( MAX541_BAD_HANDLE );

	// There is a problem with the 541 framing in that it uses the 15 MOST significant bits of 
	// the current value as the 15 LEAST significant bits to use, and the LEAST significant
	// bit of the PREVIOUS value as the MOST significant bit to use!!!
	// One workaround that doesn't pay too heavy a penalty is to shift the output value
	// to put all the bits in the right position, then output the value twice: the first time
	// to set the intended MSB in the LSB of the previous frame, and the second time to set
	// the correct values of the remaining 15 bits.

	// First arrange the bits so the MSB is in the LSB position; other bits shifted 1 left.

	if( value & 0x8000 )
		msb = 1;
	else msb =0;
	adjValue = (value << 1) + msb;

	setCSLow(devInfo);						// enable chip to rcv out data

	// first PutC guarantees that the MSB of the desired output is in the "previous" output

	SpiChnPutC(devInfo->spiChan, adjValue);	// output shifted data
	while(!SpiChnDataRdy(devInfo->spiChan))	// wait for done
		asm( "Nop;" );
	msb = SpiChnGetC(devInfo->spiChan);	// Read dummy received data; will be discarded

	// second PutC sets the remaining 15 LSBs in the right spot.  Note that the 541 does not 
	// use the LSB of this PutC; it uses the LSB from the first PutC as this PutC's MSB,
	// and "saves" this as the MSB for the next frame (which we will override in the next call)!

	SpiChnPutC(devInfo->spiChan, adjValue);	// output shifted data
	while(!SpiChnDataRdy(devInfo->spiChan))	// wait for done
		asm( "Nop;" );
	setCSHigh(devInfo);						// disable chip at end of transfer
	msb = SpiChnGetC(devInfo->spiChan);	// Read dummy received data; will be discarded

	return MAX541_SUCCESS;
}

INT16 MAX541Close( INT16 handle )
{
	MAX541DevInfo *devInfo;					// device info struct for this device

	// get the device info blk

	devInfo = (MAX541DevInfo *)handleDevInfoGet( handle );
	if( devInfo == NULL ) 
		return( MAX541_BAD_HANDLE );
	MAX541Write( handle, 0 );				// 0 output voltage
	if ( --openDevices[devInfo->spiChan] == 0 )		// decr count of open handles using this SPI channel
		SpiChnClose( devInfo->spiChan );	// no more handles; close SPI port
	free( (char *)devInfo );				// deallocate dev info block
	handleNumberClose( handle );			// deallocate handle
	return(MAX541_SUCCESS);
}
